package online.zhangwenzhe.common.security.datasource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Service
public class MenuService {


    AuthenticationResourceDao authenticationResourceDao;

    ResourceVo root;
    List<String> authorizedResource;

    @Autowired
    public MenuService(AuthenticationResourceDao authenticationResourceDao) {
        this.authenticationResourceDao = authenticationResourceDao;
        root = getMenuTree();
    }

    private void generateMenu(List<ResourceVo> resourceVoList, ResourceVo parent) {
        List<ResourceVo> children = resourceVoList
                .stream()
                .filter(x -> x.getParentId().equals(parent.getId()))
                .collect(Collectors.toList());

        parent.setChildren(children);

        for (ResourceVo child : children) {
            child.setParent(parent);
            generateMenu(resourceVoList, child);
        }
    }

    public ResourceVo getAuthorizedMenu(Long userId) {
        authorizedResource = authenticationResourceDao.getAuthorizedResource(userId);

        Consumer<ResourceVo> preOrderAction = (n) -> {
            /*
            1.直接获得权限，包含通配符的非叶子节点，和具体路径的叶子节点
            2.通过上层权限继承的权限
             */
            boolean hasPer = (n.getParent() != null && n.getParent().getHasPermission())
                    || authorizedResource.contains(n.getUri());
            n.setHasPermission(hasPer);
        };

        Consumer<ResourceVo> postOrderAction = (n) -> {
            /*
            前序遍历在后序遍历前操作节点，通过上层通配符获取到权限，以及叶子节点已经做了判断
             */
            if (n.getHasPermission()) {
                return;
            }

            /*
            不考虑上层节点包含通配符的情况：通过后序遍历自下而上判断
            1.不包含子节点即为叶子节点，直接判断是否可以访问
            2.包含子节点，要考虑下层节点有没有权限，子节点中有一个以上有权限，该节点则有权限
             */
            if (CollectionUtils.isEmpty(n.getChildren())) {

                /*
                以下判断在有前序通配符匹配的情况下可以不要，因为叶子节点已经判断过了
                 */
                //begin
                if (authorizedResource.contains(n.getUri())) {
                    n.setHasPermission(true);
                } else {
                    n.setHasPermission(false);
                }
                //end
            } else {
                if (n.getChildren().stream().anyMatch(ResourceVo::getHasPermission)) {
                    n.setHasPermission(true);
                } else {
                    n.setHasPermission(false);
                }
            }
        };

        Consumer<ResourceVo> preOrderPrint = (n) -> {
            System.out.println(n.getName() + n.getHasPermission());
        };
        TravelMenu(root, preOrderAction, postOrderAction);
        TravelMenu(root, preOrderPrint, null);
        return root;
    }

    private void TravelMenu(ResourceVo node, Consumer<ResourceVo> preOrderAction, Consumer<ResourceVo> postOrderAction) {
        if (node == null) {
            return;
        }

        //前序遍历
        if (preOrderAction != null) {
            preOrderAction.accept(node);
        }

        for (ResourceVo child : node.getChildren()) {
            TravelMenu(child, preOrderAction, postOrderAction);
        }

        //后序遍历
        if (postOrderAction != null) {
            postOrderAction.accept(node);
        }
    }

    public ResourceVo getMenuTree() {
        List<ResourceVo> resourceVoList = authenticationResourceDao.getAllResource();
        ResourceVo root = resourceVoList
                .stream()
                .filter(x -> x.getParentId() == 0)
                .findFirst()
                .orElse(null);
        generateMenu(resourceVoList, root);

        return root;
    }
}
